#include <timberyard/backtrace.h>
#include <timberyard/utils.h>
#include "config.h"

#include <sstream>
#include <dlfcn.h>

#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif

#ifdef HAVE_CXXABI_H
#include <cxxabi.h>
#endif


namespace Timberyard {

Backtrace::Backtrace(unsigned int popcount) : m_stack(0),m_depth(0) {
	Capture(popcount);
}

Backtrace& Backtrace::Capture(unsigned int popcount) {
	// remove ourself:
	// I wanted to be clever here and check the fnptrs against this and the c'tor
	// but you can't get the fnptr to a ctor, so that kinda spoilt it.
	// So, we just act dumb. Seems the ctor doesn't always produce an entry. We
	// prefer not to lose the first significant, even if that does mean we
	// could see the ctor on some platforms
	++popcount;
	delete[] (m_stack);
	m_depth=0; m_stack=0; // clear state
#ifdef HAVE_EXECINFO_H
	void** tempstack=new void*[20+popcount];
  unsigned int res=::backtrace(tempstack,20+popcount);
	if (popcount<res) {
		m_stack=new void* [res-popcount];
	  // there are some results left after the pop
		for (unsigned int i=popcount; i<res; ++i) {
			m_stack[i-popcount]=tempstack[i];
		}
		m_depth=res-popcount;
	} // no results after the pop
#endif // HAVE_EXECINFO_H
  return *this;
}

std::string Backtrace::AsString() const {
	std::stringstream result;
#ifdef _GNU_SOURCE
	Dl_info symbol;
#endif //_GNU_SORUCE
#ifdef HAVE_EXECINFO_H
	result<<"Stack backtrace:\n";
	for (unsigned int i=0; i<m_depth; ++i) {
    result << "  [" ;
		Timberyard::PrintPointer<void>(result,m_stack[i]);
		result << "] ";
		if ((0==dladdr(m_stack[i],&symbol)) || (symbol.dli_sname==0)) {
			result << "UNKNOWN SYMBOL";
		} else {
			result << symbol.dli_fname << " : ";
#ifdef HAVE_CXXABI_H
			int status;
			char* demangled=abi::__cxa_demangle(symbol.dli_sname,0,0,&status);
			if (status==0) {
				result << demangled;
				delete(demangled);
			} else if (status==-2) {
				// not a c++ name - we assume it's C
				result << symbol.dli_sname;
			} else {
				result << "SYMBOL LOOKUP FAILED ("<<status<<")";
			}
#else  // HAVE_CXXABI_H
			// we don't have any symbol demangler, so this will look ugly.
			result << symbol.dli_sname;
#endif // HAVE_CXXABI_H
		}
		result << std::endl;
	}
#else // HAVE_EXECINFO_H
	result << "Architecture does not support stack traces"<<std::endl;
#endif // HAVE_EXECINFO_H
	return std::string(result.str());
}

} // namespace Timberyard
